package top.jacktgq.lock.sync.biased;


import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.openjdk.jol.info.ClassLayout;

import java.util.Vector;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * @Author CandyWall
 * @Date 2022/3/12--12:34
 * @Description 偏向锁测试
 */
@Slf4j
public class TestBiased {
    @Test
    public void test1() {
        Dog dog = new Dog();
        log.debug(ClassLayout.parseInstance(dog).toPrintableSimple());
        // 可以看到4s以后，dog对象会变成可以偏向的状态，意味着在synchronized的时候可以加偏向锁
        try {
            TimeUnit.MILLISECONDS.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple());
    }

    @Test
    public void test2() {
        // 偏向锁是默认是延迟的，不会在程序启动时立即生效，如果想避免延迟，可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟
        Dog dog = new Dog();
        log.debug(ClassLayout.parseInstance(dog).toPrintableSimple());
    }

    @Test
    public void test3() {
        // 偏向锁是默认是延迟的，不会在程序启动时立即生效，如果想避免延迟，可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟
        // 如果不把偏向锁延迟去掉，synchronized前是普通的锁状态，synchronized中的时候默认用的是轻量级锁，synchronized后会解锁
        // 如果去掉延迟，synchronized前是可偏向状态，synchronized中的时候默认用的是偏向锁，并记录绑定对应线程ID，synchronized后会解锁，但线程 id 仍存储于对象头中
        // 还可以用-XX:-UseBiasedLocking来禁用对象锁
        Dog dog = new Dog();
        log.debug("synchronized 前");
        System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
        synchronized (dog) {
            log.debug("synchronized 中");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
        }
        log.debug("synchronized 后");
        System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
    }

    @Test
    // 撤销偏向锁：情况1，调用hashCode()方法
    public void test4() {
        // 先禁用偏向锁延迟 -XX:BiasedLockingStartupDelay=0
        Dog dog = new Dog();
        dog.hashCode();
        // 调用一次hashCode()方法会撤销掉这个对象的偏向锁
        log.debug("synchronized 前");
        System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
        synchronized (dog) {
            log.debug("synchronized 中");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
        }
        log.debug("synchronized 后");
        System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
    }

    @Test
    // 撤销偏向锁：情况2，调用hashCode()方法
    public void test5() {
        Dog dog = new Dog();
        // 先禁用偏向锁延迟 -XX:BiasedLockingStartupDelay=0
        Thread t1 = new Thread(() -> {
            log.debug("synchronized 前");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
            synchronized (dog) {
                log.debug("synchronized 中");
                System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
            }
            log.debug("synchronized 后");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
            synchronized (TestBiased.class) {
                // 唤醒正在wait()中的线程t2
                TestBiased.class.notify();
            }
            // 隐患？？？t1一口气执行完了，而此时t2还没有运行到wait()处，t2再等待将无法被唤醒。
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (TestBiased.class) {
                // 等待线程t1先访问dog对象，避免竞争让锁膨胀为重量级锁
                try {
                    TestBiased.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("synchronized 前");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
            synchronized (dog) {
                log.debug("synchronized 中");
                System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
            }
            log.debug("synchronized 后");
            System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());

        }, "t2");
        t2.start();

        log.debug("主线程");
        // System.out.println(ClassLayout.parseInstance(dog).toPrintableSimple());
        try {
            // 等两个线程都结束再退出
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    // 批量重偏向
    public void test6() {
        Vector<Dog> list = new Vector<>();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                }
            }
            synchronized (list) {
                list.notify();
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (list) {
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 30; i++) {
                Dog d = list.get(i);
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                synchronized (d) {
                    log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                }
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
            }
        }, "t2");
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    Thread t1 = null, t2 = null, t3 = null;
    @Test
    // 批量撤销
    public void test7() {

        Vector<Dog> list = new Vector<>();
        int loopNumber = 39;
        t1 = new Thread(() -> {
            for (int i = 0; i < loopNumber; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                }
            }
            LockSupport.unpark(t2);
        }, "t1");


        t2 = new Thread(() -> {
            LockSupport.park();
            for (int i = 0; i < loopNumber; i++) {
                Dog d = list.get(i);
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                synchronized (d) {
                    log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                }
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
            }
            LockSupport.unpark(t3);
        }, "t2");


        t3 = new Thread(() -> {
            LockSupport.park();
            for (int i = 0; i < loopNumber; i++) {
                Dog d = list.get(i);
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                synchronized (d) {
                    log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
                }
                log.debug(i + " \t" + ClassLayout.parseInstance(d).toPrintableSimple());
            }
        }, "t3");

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 频繁的批量锁撤销超过40次这个阈值，JDK会认为当前类的对象竞争比较激烈，以后该类对象的创建都将默认为不可偏向。
        log.debug(40 + " \t" + ClassLayout.parseInstance(new Dog()).toPrintableSimple());
    }
}


